﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace SmokinAces.Object_Classes.Objects
{
    public class Main_Character : Game_Object, Draw_Object, Logic_Object, Modify_Object
    {
        //------this is information for the editor...
        static Type[] constructor_parameters = { typeof(int), typeof(int), typeof(Main_Engine) };
        static object[] default_const_values = { 0, 0, null };
        //------------------------------------
        VertexPositionColorTexture[] baseImage;
		VertexPositionColorTexture[] vertices;
		int[] indecies;
        private Sort_Delegate m_Sort_Delegate;
        Vector3 m_Position;
        Dictionary<String, int> Texture_Positions = new Dictionary<string, int>();
        List<String> WalkingSounds = new List<string>();
        int currentSound = 0;
        int currentGraphic;
        private int _currentHealth = 100;
        private const float divider = 1.0f / 20;
        private bool standing = true;
        private bool sword = false;
        private bool swinging = false;
        private int swing = 0;
        private int spellTimer = 0;
        private int numOfActiveSpells = 0;

        public int currentHealth
        {
            get { return _currentHealth; }
            set
            {
                _currentHealth = value;
                if (_currentHealth <= 0)
                {
                    SoundManager.PlayMusic("GameOver");
                    _currentHealth = 0;
                }
                if (this.currentHealth > _maxHealth)
                {
                    this.currentHealth = _maxHealth;
                }
            }
        }
        private int _maxHealth = 100;
        public int maxHealth
        {
            get { return _maxHealth; }
            set { _maxHealth = value; }
        }
        private int _currentMana = 100;
        public int currentMana
        {
            get { return _currentMana; }
            set {
                _currentMana = value;
                if (_currentMana <= 0)
                {
                    _currentMana = 0;
                }
                if (_currentMana > _maxMana)
                {
                    _currentMana = _maxMana;
                }
            }
        }
        private int _maxMana = 100;
        public int maxMana
        {
            get { return _maxMana; }
            set { _maxMana = value; }
        }
        private int _currentMoney = 999;
        public int currentMoney
        {
            get { return _currentMoney; }
            set { _currentMoney = value; }
        }

        public Main_Character(int left, int top, Main_Engine Parent)
        {
            Vector3 Position = new Vector3(left, top, 1.0f);
            Initialize(Position, Parent);
        }
        public Main_Character(Vector3 Position, Main_Engine parent)
        {
            Initialize(Position, parent);
        }
        public Main_Character(XmlReader xmlReader, Main_Engine Parent)
        {
            xmlReader.Read();
            string sLeft = xmlReader.ReadElementString("Left");
            string sTop = xmlReader.ReadElementString("Top");
            string sHealth = xmlReader.ReadElementString("cHealth");
            string sMana = xmlReader.ReadElementString("cMana");
            string sMoney = xmlReader.ReadElementString("Money");
            _currentHealth = Convert.ToInt32(sHealth);
            _currentMana = Convert.ToInt32(sMana);
            _currentMoney = Convert.ToInt32(sMoney);
            //------------------------------------
            int left = Convert.ToInt32(sLeft);
            int top = Convert.ToInt32(sTop);
            Vector3 Pos = new Vector3(left, top, 1.0f);
            Initialize(Pos, Parent);
        }
        public override bool Save_XML(XmlTextWriter textWriter)
        {
            try
            {
                textWriter.WriteStartElement("Hero");
                textWriter.WriteAttributeString("Type", this.GetType().AssemblyQualifiedName);
                textWriter.WriteElementString("Left", baseImage[0].Position.X.ToString());
                textWriter.WriteElementString("Top", baseImage[0].Position.Y.ToString());
                textWriter.WriteElementString("cHealth", _currentHealth.ToString());
                textWriter.WriteElementString("cMana", _currentMana.ToString());
                textWriter.WriteElementString("Money", _currentMoney.ToString());
                textWriter.WriteEndElement();
                textWriter.Flush();
            }
            catch
            {
                return false;
            }
            return true;
        }
        private void Initialize(Vector3 Position, Main_Engine parent)
        {
            this.parent = parent;
            this.m_Position = Position;
            GraphicBB = new BoundingBox(Position, Position + new Vector3(64, 64, Util.MaxZ));

            Color baseColor = new Color(255, 255, 255, 255);

            baseImage = new VertexPositionColorTexture[4];
            baseImage[0].Color = baseColor;
            baseImage[1].Color = baseColor;
            baseImage[2].Color = baseColor;
            baseImage[3].Color = baseColor;
            AnimationList.Clear();
            AnimationList.AddRange(WalkSouthAnimationList);

            Texture_Positions.Add("hero_walk", parent.register_texture("Sprites//Main//hero_walk.png"));
            Texture_Positions.Add("hero_walk_sword", parent.register_texture("Sprites//Main//hero_walk_sword.png"));
            Texture_Positions.Add("hero_run", parent.register_texture("Sprites//Main//hero_run.png"));
            Texture_Positions.Add("hero_run_sword", parent.register_texture("Sprites//Main//hero_run_sword.png"));
            Texture_Positions.Add("hero_swing_sword", parent.register_texture("Sprites//Main//hero_swing_sword.png"));

            //Audio'
            WalkingSounds.Add("Step1");
            WalkingSounds.Add("Step2");
            WalkingSounds.Add("Step3");


            recalculate();

            Bounding_Regions.Add(new BoundingBox(new Vector3(Position.X + 25, Position.Y + 15, -1), new Vector3(Position.X + 55, Position.Y + 30, 1)));
            m_Sort_Delegate = new Sort_Delegate(((1024 - (Position.Y -15)) * 1024) + Position.X, Draw);
            //----------------------------------
        }

        //-------used as an indication of which directing to swing your sword
        int swingOffset = 0;

        /// <summary>
        /// Decide which graphic and what coordinates to draw
        /// </summary>
        private void recalculate()
        {
            const float zorder = 1.0f;
            float offset = 0.0f;

            if (swinging)
            {
                currentGraphic = Texture_Positions["hero_swing_sword"];
                Swing_Sequence++;
                if (Swing_Sequence >= AnimationList.Count())
                {
                    swinging = false;
                    Bounding_Weapon.Clear();
                    switch (swingOffset)
                    {
                        case 0:
                            lastactions = Actions.south;
                            break;
                        case 4:
                            lastactions = Actions.north;
                            break;
                        case 8:
                            lastactions = Actions.east;
                            break;
                        case 12:
                            lastactions = Actions.west;
                            break;
                    }
                    this.SetStandingDirection();
                    recalculate();
                }

                else
                {
                    offset = (AnimationList[Swing_Sequence] + swingOffset) * divider;
                    if (Bounding_Weapon.Count == 0)
                    {
                        switch (swingOffset)
                        {
                            case 0:
                                Bounding_Weapon.Add(new BoundingBox(new Vector3(m_Position.X + 10, m_Position.Y, -1), new Vector3(m_Position.X + 75, m_Position.Y + 35, 1)));
                                break;
                            case 4:
                                Bounding_Weapon.Add(new BoundingBox(new Vector3(m_Position.X + 10, m_Position.Y + 35, -1), new Vector3(m_Position.X + 75, m_Position.Y + 85, 1)));
                                lastactions = Actions.north;
                                break;
                            case 8:
                                Bounding_Weapon.Add(new BoundingBox(new Vector3(m_Position.X + 45, m_Position.Y + 10, -1), new Vector3(m_Position.X + 85, m_Position.Y + 75, 1)));
                                lastactions = Actions.east;
                                break;
                            case 12:
                                Bounding_Weapon.Add(new BoundingBox(new Vector3(m_Position.X, m_Position.Y + 10, -1), new Vector3(m_Position.X + 40, m_Position.Y + 75, 1)));
                                lastactions = Actions.west;
                                break;
                        }
                    }
                    
                }
            }
            else
            {
                switch (lastactions)
                {
                    case Actions.none:
                    case Actions.draw_sword:
                    case Actions.east:
                    case Actions.north:
                    case Actions.south:
                    case Actions.west:
                        if (sword)
                        {
                            currentGraphic = Texture_Positions["hero_walk_sword"];
                        }
                        else if (!sword)
                        {
                            currentGraphic = Texture_Positions["hero_walk"];
                        }
                        break;

                    case Actions.run_east:
                    case Actions.run_north:
                    case Actions.run_south:
                    case Actions.run_west:
                        if (sword)
                        {
                            currentGraphic = Texture_Positions["hero_run_sword"];
                        }
                        else if (!sword)
                        {
                            currentGraphic = Texture_Positions["hero_run"];
                        }

                        break;
                }
                if (standing)
                {
                    offset = AnimationList[0] * divider;
                }
                else
                {
                    if (AnimationList.Count > Animation_Sequence)
                    {
                        offset = AnimationList[Animation_Sequence] * divider;
                    }
                }
            }
            baseImage[0].Position = new Vector3(m_Position.X, m_Position.Y, zorder);
            baseImage[0].TextureCoordinate = new Vector2(offset, 0.99f);
            baseImage[1].Position = new Vector3(m_Position.X, m_Position.Y + 85, zorder);
            baseImage[1].TextureCoordinate = new Vector2(offset, 0.01f);
            baseImage[2].Position = new Vector3(m_Position.X + 85, m_Position.Y, zorder);
            baseImage[2].TextureCoordinate = new Vector2(offset - divider, 0.99f);
            baseImage[3].Position = new Vector3(m_Position.X + 85, m_Position.Y + 85, zorder);
            baseImage[3].TextureCoordinate = new Vector2(offset - divider, 0.01f);
            vertices = Util.ComplexMesh(baseImage, out indecies, 4, 4);
        }

        #region Draw_Object Members

        public void Draw(Microsoft.Xna.Framework.GameTime gameTime, Microsoft.Xna.Framework.Graphics.GraphicsDevice device)
        {
            //---Set the Texture for this layer
            parent.effect.BaseTexture = parent.Texture_List[currentGraphic];

            //---------Begin the drawing passes
            parent.effect.Begin();
            foreach (EffectPass pass in parent.effect.CurrentTechnique.Passes)
            {
                pass.Begin();
                //device.DrawUserPrimitives(PrimitiveType.TriangleStrip, baseImage, 0, 2);
				device.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, vertices, 0, vertices.Length, indecies, 0, indecies.Length/3);
                pass.End();
            }
            parent.effect.End();
        }
        public bool startDraw()
        {
            parent.Draw_Functions[3].Add(m_Sort_Delegate);
            return true;
        }
        public bool stopDraw()
        {
            parent.Draw_Functions[3].Remove(m_Sort_Delegate);
            return true;
        }
        #endregion

        #region Game_Object Members

        public override bool dispose()
        {
            return stopUpdate() && stopDraw();
        }

        #endregion

        #region Logic_Object Members

        double LastAnimation = 0.0;
        double LastSound = 0.0;
        int Animation_Sequence = 1;
        int Swing_Sequence = 1;
        int update = 60;
        int sound_update = 256;

        //Animation lists
        int[] WalkSouthAnimationList = { 6, 8, 8, 7, 7, 7, 8, 8, 10, 9, 9, 9, 10, 8};
        int[] WalkNorthAnimationList = { 1, 3, 3, 2, 2, 2, 5, 5, 4, 4, 4, 3, 3 };
        int[] WalkEastAnimationList = { 12, 12, 13, 13, 13, 14, 14, 14, 13, 13, 13, 12 };
        int[] WalkWestAnimationList = { 19, 19, 18, 18, 18, 17, 17, 17, 18, 18, 18, 19 };

        int[] RunSouthAnimationList = { 5, 5, 6, 6, 6, 5, 5, 7, 7, 8, 8, 8, 7, 7 };
        int[] RunNorthAnimationList = { 1, 1, 2, 2, 2, 1, 1, 3, 3, 4, 4, 4, 3, 3 };
        int[] RunWestAnimationList = { 9, 9, 10, 10, 10, 9, 9, 12, 12, 11, 11, 11, 12, 12 };
        int[] RunEastAnimationList = { 16, 16, 15, 15, 15, 16, 16, 13, 13, 14, 14, 14, 13, 13 };

        int[] RunWithSwordSouthAnimationList = { 7, 7, 8, 8, 8, 10, 10, 9, 9, 11, 11, 11, 7 };
        int[] RunWithSwordNorthAnimationList = { 1, 1, 3, 3, 5, 3, 1, 4, 4, 6, 6, 2, 6, 4 };
        int[] RunWithSwordWestAnimationList = { 19, 19, 20, 20, 20, 19, 19, 18, 18, 18, 19, 19 };
        int[] RunWithSwordEastAnimationList = { 12, 12, 13, 13, 13, 12, 12, 15, 15, 15, 12, 12 };

        int[] SwingSordAnimationList = { 1, 1, 2, 2, 3, 3, 4, 4 };


        Vector3 Speed = Vector3.Zero;
        List<int> AnimationList = new List<int>();

        public void Update(Microsoft.Xna.Framework.GameTime gameTime)
        {
            //System.Console.WriteLine(gameTime.TotalGameTime.TotalMilliseconds - LastAnimation + update);
            //System.Threading.Thread.Sleep((int)(gameTime.TotalGameTime.TotalMilliseconds - LastAnimation));
            if (spellTimer > 0) spellTimer--;
            
            if (gameTime.TotalGameTime.TotalMilliseconds > LastAnimation + update)
            {
                if (lastactions != Actions.none && lastactions != Actions.draw_sword && !swinging)
                {
                    //BoundingBox oldBox = new BoundingBox(GraphicBB.Min, GraphicBB.Max);
                    //Here is what I'm going to do move the bounding box in the direction you want to go
                    //then check for colisions, but stretch instead of move.
                    updateBoundingRegions(Speed);
                    if (isColliding())
                    {
                        HashSet<Game_Object> crash = this.isCollidingWith(Bounding_Regions[0], Collision_Type.BoundingVolume);
                        if (crash.OfType<NPC>().Count() > 0)
                        {
                            if (currentMoney > 250)
                            {
                                this.currentMoney -= 250;
                                this.maxHealth += 10;
                            }
                        }
                        if (crash.OfType<Item>().Count() > 0)
                        {
                            foreach (Item p in crash.OfType<Item>())
                            {
                                switch (p.currentItem)
                                {
                                    case Item_Type.Money:
                                        this.currentMoney += p.increase;
                                        break;
                                    case Item_Type.Mana:
                                        this.currentMana += p.increase;
                                        break;
                                    case Item_Type.Health:
                                        this.currentHealth += p.increase;
                                        break;
                                }
                                parent.MapObjects.Remove(p);
                            }
                        }
                        updateBoundingRegions(-Speed);
                    }
                    else
                    {
                        Animation_Sequence++;
                        if (Animation_Sequence >= AnimationList.Count())
                        {
                            Animation_Sequence = 1;
                        }
                        m_Position += Speed;
                        parent.CurrentScreen.Min += Speed;
                        parent.CurrentScreen.Max += Speed;
                        m_Sort_Delegate.order = ((1024 - (m_Position.Y + 18)) * 1024) + m_Position.X;
                    }
                }
                if (this.isColliding())
                {
                    HashSet<Game_Object> crash = this.isCollidingWith(Bounding_Regions[0], Collision_Type.Weapon);
                    if (crash.OfType<Fireball>().Count() > 0)
                    {
                        foreach (Fireball f in crash.OfType<Fireball>())
                        {
                            this.currentHealth -= 10;
                        }
                    }
                    //Make him just stand there 
                    this.SetStandingDirection();
                }

                  
                recalculate();

                LastAnimation = gameTime.TotalGameTime.TotalMilliseconds;
            }

            //Play the walking sounds
            if (gameTime.TotalGameTime.TotalMilliseconds > LastSound + sound_update)
            {
                if (lastactions != Actions.none)
                {

                    SoundManager.PlaySound(WalkingSounds[currentSound]);
                    currentSound++;
                    if (currentSound >= WalkingSounds.Count)
                    {
                        currentSound = 0;
                    }
                }
                LastSound = gameTime.TotalGameTime.TotalMilliseconds;
            }
            
        }
        public bool startUpdate()
        {
            parent.add_Update_Object(Update);
            return true;
        }
        public bool stopUpdate()
        {
            parent.remove_Update_Object(Update);
            return true;
        }
        #endregion

        #region Modify_Object Members
        Actions lastactions = Actions.none;
        Actions lastMovement = Actions.south;

        public void interact(Actions modify)
        {
            //when you press space, check to see if your sword is out, then set the animation list if it is.  If not, just ignore this action
            if (modify == Actions.swing_sword)
            {
                    if (sword)
                    {
                        swinging = true;
                        SoundManager.PlaySound("Sword");
                        Swing_Sequence = 1;
                        AnimationList.Clear();
                        AnimationList.AddRange(SwingSordAnimationList);
                    }
                    else
                    {
                        modify = lastactions;
                    }
                   
                
                
            }
            //While swinging, don't process any other interactions
            if (!swinging)
            {
                if (modify == Actions.none)
                {
                    this.SetStandingDirection();
                }
                else if (modify == Actions.draw_sword)
                {
                    sword = !sword;
                    this.SetStandingDirection();
                }
                else if (swinging == false)
                {
                    standing = false;
                    //fill animation lists with the appropriate lists
                    switch (modify)
                    {
                        case Actions.east:
                            update = 60;
                            AnimationList.Clear();
                            AnimationList.AddRange(WalkEastAnimationList);
                            Speed = new Vector3(2.0f, 0.0f, 0.0f);
                            break;
                        case Actions.north:
                            update = 60;
                            AnimationList.Clear();
                            AnimationList.AddRange(WalkNorthAnimationList);
                            Speed = new Vector3(0.0f, 2.0f, 0.0f);
                            break;
                        case Actions.west:
                            update = 60;
                            AnimationList.Clear();
                            AnimationList.AddRange(WalkWestAnimationList);
                            Speed = new Vector3(-2.0f, 0.0f, 0.0f);
                            break;
                        case Actions.south:
                            update = 60;
                            AnimationList.Clear();
                            AnimationList.AddRange(WalkSouthAnimationList);
                            Speed = new Vector3(0.0f, -2.0f, 0.0f);
                            break;
                        case Actions.run_east:
                            update = 20;
                            AnimationList.Clear();
                            if (sword)
                            {
                                AnimationList.AddRange(RunWithSwordEastAnimationList);
                            }
                            else
                            {
                                AnimationList.AddRange(RunEastAnimationList);
                            }
                            Speed = new Vector3(6.0f, 0.0f, 0.0f);
                            break;
                        case Actions.run_north:
                            update = 30;
                            AnimationList.Clear();
                            if (sword)
                            {
                                AnimationList.AddRange(RunWithSwordNorthAnimationList);
                            }
                            else
                            {
                                AnimationList.AddRange(RunNorthAnimationList);
                            }
                            Speed = new Vector3(0.0f, 6.0f, 0.0f);
                            break;
                        case Actions.run_west:
                            update = 20;
                            AnimationList.Clear();
                            if (sword)
                            {
                                AnimationList.AddRange(RunWithSwordWestAnimationList);
                            }
                            else
                            {
                                AnimationList.AddRange(RunWestAnimationList);
                            }
                            Speed = new Vector3(-6.0f, 0.0f, 0.0f);
                            break;
                        case Actions.run_south:
                            update = 30;
                            AnimationList.Clear();
                            if (sword)
                            {
                                AnimationList.AddRange(RunWithSwordSouthAnimationList);
                            }
                            else
                            {
                                AnimationList.AddRange(RunSouthAnimationList);
                            }
                            Speed = new Vector3(0.0f, -6.0f, 0.0f);
                            break;
                        case Actions.none:
                        case Actions.draw_sword:
                            Speed = Vector3.Zero;
                            break;
                    }

                }

                //Set the swing offset after every interaction so you can remember which direction to swing if you decide to swing
                switch (lastactions)
                {
                    //swingOffset corresponds to the position of the swing animaiton in the hero_swing_sword graphic
                    case Actions.west:
                    case Actions.run_west:
                        swingOffset = 12;
                        break;
                    case Actions.east:
                    case Actions.run_east:
                        swingOffset = 8;
                        break;
                    case Actions.north:
                    case Actions.run_north:
                        swingOffset = 4;
                        break;
                    case Actions.south:
                    case Actions.run_south:
                        swingOffset = 0;
                        break;

                }
                lastactions = modify;
                if (lastactions != Actions.none) lastMovement = lastactions;
            }
        }

        /// <summary>
        /// Helper method to set the graphic to the proper standing position
        /// </summary>
        private void SetStandingDirection()
        {
            Animation_Sequence = 1;
            standing = true;
            
            switch (lastactions)
            {
                case Actions.east:
                    AnimationList.Clear();
                    AnimationList.Add(11);
                    break;
                case Actions.north:
                    AnimationList.Clear();
                    AnimationList.Add(1);
                    break;
                case Actions.west:
                    AnimationList.Clear();
                    AnimationList.Add(20);
                    break;
                case Actions.south:
                    AnimationList.Clear();
                    AnimationList.Add(6);
                    break;
                case Actions.run_east:
                    AnimationList.Clear();
                    AnimationList.Add(11);
                    break;
                case Actions.run_north:
                    AnimationList.Clear();
                    AnimationList.Add(1);
                    break;
                case Actions.run_west:
                    AnimationList.Clear();
                    AnimationList.Add(20);
                    break;
                case Actions.run_south:
                    AnimationList.Clear();
                    AnimationList.Add(6);
                    break;
            }
            lastactions = Actions.none;
        }
        public bool castIceBolt(Game_Engine gameEngine)
        {
            bool cast = false;

            if (spellTimer == 0)
            {
                spellTimer = 30;
                int direction = 0;
                int xOffset = 0;
                int yOffset = 0;

                if (this.lastMovement == Actions.east || this.lastMovement == Actions.run_east)
                {
                    direction = 0;
                    yOffset = 10;
                    xOffset = 65;
                }
                else if (this.lastMovement == Actions.west || this.lastMovement == Actions.run_west)
                {
                    direction = 1;
                    yOffset = 10;
                    xOffset = -32;
                }
                else if (this.lastMovement == Actions.north || this.lastMovement == Actions.run_north)
                {
                    direction = 2;
                    yOffset = 60;
                    xOffset = 10;
                }
                else if (this.lastMovement == Actions.south || this.lastMovement == Actions.run_south)
                {
                    direction = 3;
                    yOffset = -28;
                    xOffset = 10;
                }
                this.currentMana -= 1;

                IceBolt Blizzard = new IceBolt((int)this.m_Position.X + xOffset, (int)this.m_Position.Y + yOffset, gameEngine);
                Blizzard.direction = direction;
                Blizzard.startDraw();
                Blizzard.startUpdate();

                numOfActiveSpells++;

                cast = true;
            }
            else cast = false;

            return cast;
        }

        public bool castFireball(Game_Engine gameEngine)
        {
            bool cast = false;

            if (spellTimer == 0)
            {
                spellTimer = 30;
                int direction = 0;
                int xOffset = 0;
                int yOffset = 0;

                if (this.lastMovement == Actions.east || this.lastMovement == Actions.run_east)
                {
                    direction = 0;
                    yOffset = 20;
                    xOffset = 60;
                }
                else if (this.lastMovement == Actions.west || this.lastMovement == Actions.run_west)
                {
                    direction = 1;
                    yOffset = 20;
                    xOffset = -5;
                }
                else if (this.lastMovement == Actions.north || this.lastMovement == Actions.run_north)
                {
                    direction = 2;
                    yOffset = 43;
                    xOffset = 27;
                }
                else if (this.lastMovement == Actions.south || this.lastMovement == Actions.run_south)
                {
                    direction = 3;
                    xOffset = 27;
                    yOffset = -15;
                }
                this.currentMana -= 10;

                Fireball fireBall = new Fireball((int)this.m_Position.X + xOffset, (int)this.m_Position.Y + yOffset, gameEngine);
                fireBall.direction = direction;
                fireBall.startDraw();
                fireBall.startUpdate();

                numOfActiveSpells++;

                cast = true;
            }
            else cast = false;

            return cast;
        }

        #endregion

        private enum Direction_Enum
        {
            north,
            west,
            south,
            east
        };
    }

}


